﻿using Abp;
using Abp.Application.Editions;
using Abp.Application.Features;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.BackgroundJobs;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Runtime.Session;
using Abp.UI;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Partner.Surround.Authorization;
using Partner.Surround.Editions.Dto;
using Partner.Surround.MultiTenancy;

namespace Partner.Surround.Editions
{
    /// <summary>
    /// 版本管理应用服务实现
    /// </summary>
    public class EditionAppService : SurroundAppServiceBase, IEditionAppService
    {
        #region 初始化
        private readonly EditionManager _editionManager;
        private readonly IRepository<Edition> _editionRepository;
        private readonly IRepository<Tenant> _tenantRepository;
        private readonly IBackgroundJobManager _backgroundJobManager;

        public EditionAppService(
            EditionManager editionManager,
            IRepository<Edition> editionRepository,
            IRepository<Tenant> tenantRepository,
            IBackgroundJobManager backgroundJobManager)
        {
            _editionManager = editionManager;
            _editionRepository = editionRepository;
            _tenantRepository = tenantRepository;
            _backgroundJobManager = backgroundJobManager;
        }
        #endregion

        #region 版本管理
        [AbpAuthorize(PermissionNames.Pages_Frame_Editions)]
        public async Task<ListResultDto<EditionListDto>> GetEditionsList()
        {
            var editions = await _editionRepository.GetAllListAsync();

            return new ListResultDto<EditionListDto>(
                editions.Select(item =>
                {
                    var dto = ObjectMapper.Map<EditionListDto>(item);
                    dto.LastModificationTime = item.LastModificationTime.HasValue ? item.LastModificationTime.Value : item.CreationTime;
                    return dto;
                }).ToList());
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_Editions_Create, PermissionNames.Pages_Frame_Editions_Update)]
        public async Task<GetEditionForEditOutput> GetEditionForEdit(NullableIdDto input)
        {
            var features = FeatureManager.GetAll()
                .Where(f => f.Scope.HasFlag(FeatureScopes.Edition));

            EditionEditDto editionEditDto;
            List<NameValue> featureValues;

            if (input.Id.HasValue)
            {
                var edition = await _editionManager.FindByIdAsync(input.Id.Value);
                featureValues = (await _editionManager.GetFeatureValuesAsync(input.Id.Value)).ToList();
                editionEditDto = ObjectMapper.Map<EditionEditDto>(edition);
            }
            else
            {
                editionEditDto = new EditionEditDto();
                featureValues = features.Select(f => new NameValue(f.Name, f.DefaultValue)).ToList();
            }

            var featureDtos = ObjectMapper.Map<List<FlatFeatureDto>>(features).OrderBy(f => f.DisplayName).ToList();

            return new GetEditionForEditOutput
            {
                Edition = editionEditDto,
                Features = featureDtos,
                FeatureValues = featureValues.Select(fv => new NameValueDto(fv)).ToList()
            };
        }

        public async Task CreateOrUpdateEdition(CreateOrUpdateEditionInput input)
        {
            #region 功能项加载,防止页面数据伪造    
            var featureValues = FeatureManager.GetAll()
                .Where(f => f.Scope.HasFlag(FeatureScopes.Edition))
                .Select(f => new NameValue(f.Name, f.DefaultValue))
                .ToList();

            foreach (var featureValue in featureValues)
            {
                foreach (var item in input.FeatureValues)
                {
                    if (featureValue.Name == item.Name)
                    {
                        featureValue.Value = item.Value;
                        break;
                    }
                }
            }
            input.FeatureValues = featureValues.Select(fv => new NameValueDto(fv)).ToList();
            #endregion

            if (input.Edition.Id != null)
            {
                await UpdateEdition(input);
            }
            else
            {
                await CreateEdition(input);
            }
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_Editions_Create)]
        private async Task CreateEdition(CreateOrUpdateEditionInput input)
        {
            var edition = ObjectMapper.Map<Edition>(input.Edition);

            await _editionManager.CreateAsync(edition);
            await CurrentUnitOfWork.SaveChangesAsync();

            await SetFeatureValues(edition, input.FeatureValues);
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_Editions_Update)]
        private async Task UpdateEdition(CreateOrUpdateEditionInput input)
        {
            var edition = await _editionManager.GetByIdAsync(input.Edition.Id.Value);
            ObjectMapper.Map(input.Edition, edition);

            await SetFeatureValues(edition, input.FeatureValues);
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_Editions_Delete)]
        public async Task DeleteEdition(EntityDto input)
        {
            var tenantCount = await _tenantRepository.CountAsync(t => t.EditionId == input.Id);
            if (tenantCount > 0)
            {
                throw new UserFriendlyException(L("ThereAreTenantsSubscribedToThisEdition"));
            }

            var edition = await _editionManager.GetByIdAsync(input.Id);
            await _editionManager.DeleteAsync(edition);
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_Editions_MoveTenantsToAnotherEdition)]
        public async Task MoveTenantsToAnotherEdition(MoveTenantsToAnotherEditionDto input)
        {
            await _backgroundJobManager.EnqueueAsync<MoveTenantsToAnotherEditionJob, MoveTenantsToAnotherEditionJobArgs>(new MoveTenantsToAnotherEditionJobArgs
            {
                SourceEditionId = input.SourceEditionId,
                TargetEditionId = input.TargetEditionId,
                User = AbpSession.ToUserIdentifier()
            });
        }
        #endregion

        #region 辅助方法
        private Task SetFeatureValues(Edition edition, List<NameValueDto> featureValues)
        {
            return _editionManager.SetFeatureValuesAsync(edition.Id, featureValues.Select(fv => new NameValue(fv.Name, fv.Value)).ToArray());
        }
        #endregion
    }
}
